/*
 * $Id$
 *
 * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com
 * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com
 *
 * The initial version of this code was written by Dragos Vingarzan
 * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
 * Fraunhofer FOKUS Institute. It was and still is maintained in a separate
 * branch of the original SER. We are therefore migrating it to
 * Kamailio/SR and look forward to maintaining it from here on out.
 * 2011/2012 Smile Communications, Pty. Ltd.
 * ported/maintained/improved by
 * Jason Penton (jason(dot)penton(at)smilecoms.com and
 * Richard Good (richard(dot)good(at)smilecoms.com) as part of an
 * effort to add full IMS support to Kamailio/SR using a new and
 * improved architecture
 *
 * NB: A lot of this code was originally part of OpenIMSCore,
 * FhG Fokus.
 * Copyright (C) 2004-2006 FhG Fokus
 * Thanks for great work! This is an effort to
 * break apart the various CSCF functions into logically separate
 * components. We hope this will drive wider use. We also feel
 * that in this way the architecture is more complete and thereby easier
 * to manage in the Kamailio/SR environment
 *
 * This file is part of Kamailio, a free SIP server.
 *
 * Kamailio is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version
 *
 * Kamailio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

/**
 * \file
 *
 * Interrogating-CSCF - Database operations
 *
 *
 *  \author Dragos Vingarzan vingarzan -at- fokus dot fraunhofer dot de
 *
 */

#include "db.h"

#include "../../lib/srdb1/db.h"
#include "../../core/mem/shm_mem.h"

#include "ims_icscf_mod.h"

static db_func_t dbf;				   /**< db function bindings*/
extern char *ims_icscf_db_url;		   /**< DB URL */
extern char *ims_icscf_db_nds_table;   /**< NDS table in DB */
extern char *ims_icscf_db_scscf_table; /**< S-CSCF table in db */
extern char *
		ims_icscf_db_capabilities_table; /**< S-CSCF capabilities table in db */

static db1_con_t *hdl_db = 0; /**< handle for the database queries */


/**
 *  Bind to the database module.
 * @param db_url - URL of the database
 * @returns 0 on success, -1 on error
 */
int ims_icscf_db_bind(char *db_url)
{

	str db_url_str = {db_url, strlen(db_url)};

	if(db_bind_mod(&db_url_str, &dbf) < 0) {
		LM_ERR("ims_icscf_db_bind: cannot bind to database module! "
			   "Did you forget to load a database module ?\n");
		return -1;
	}
	return 0;
}


/**
 *  Init the database connection
 * @param db_url - URL of the database
 * @param db_table_nds - name of the NDS table
 * @param db_table_scscf - name of the S-CSCF table
 * @param db_table_capabilities - name of the S-CSCF capabilities table
 * @returns 0 on success, -1 on error
 */
int ims_icscf_db_init(char *db_url, char *db_table_nds, char *db_table_scscf,
		char *db_table_capabilities)
{
	str db_url_str = {db_url, strlen(db_url)};

	if(dbf.init == 0) {
		LM_ERR("BUG:cscf_db_init: unbound database module\n");
		return -1;
	}

	hdl_db = dbf.init(&db_url_str);

	if(hdl_db == 0) {
		LM_ERR("ERR:ims_icscf_db_init: cannot initialize database "
			   "connection\n");
		goto error;
	}

	return 0;

error:
	if(hdl_db) {
		dbf.close(hdl_db);
		hdl_db = 0;
	}
	return -1;
}

/**
 *  Close the database connection.
 */
void ims_icscf_db_close()
{
	if(!dbf.close)
		return;
	if(hdl_db) {
		dbf.close(hdl_db);
		hdl_db = 0;
	}
}

/**
 * Simply check if the database connection was initialized and connect if not.
 * @param db_hdl - database handle to test
 * @returns 1 if connected, 0 if not
 */
static inline int ims_icscf_db_check_init(db1_con_t *db_hdl)
{
	if(db_hdl)
		return 1;
	return (ims_icscf_db_init(ims_icscf_db_url, ims_icscf_db_nds_table,
					ims_icscf_db_scscf_table, ims_icscf_db_capabilities_table)
			== 0);
}


static str s_trusted_domain = {"trusted_domain", 14};
/**
 *  Get the NDS list from the database.
 * @param d - array of string to fill with the db contents
 * @returns 1 on success, 0 on error
 */
int ims_icscf_db_get_nds(str *d[])
{
	db_key_t keys_ret[] = {&s_trusted_domain};
	db1_res_t *res = 0;
	str db_table_nds_str = {
			ims_icscf_db_nds_table, strlen(ims_icscf_db_nds_table)};

	str s;
	int i;

	if(!ims_icscf_db_check_init(hdl_db))
		goto error;

	LM_DBG("DBG:ims_icscf_db_get_nds: fetching list of NDS for I-CSCF \n");

	if(dbf.use_table(hdl_db, &db_table_nds_str) < 0) {
		LM_ERR("ERR:ims_icscf_db_init: cannot select table \"%s\"\n",
				db_table_nds_str.s);
		goto error;
	}

	if(dbf.query(hdl_db, 0, 0, 0, keys_ret, 0, 1, NULL, &res) < 0) {
		LM_ERR("ERR:ims_icscf_db_get_nds: db_query failed\n");
		goto error;
	}

	if(res->n == 0) {
		LM_DBG("DBG:ims_icscf_db_get_nds: I-CSCF has no NDS trusted domains in "
			   "db\n");
		*d = shm_malloc(sizeof(str));
		if(*d == NULL) {
			LM_ERR("ERR:ims_icscf_db_get_nds: failed shm_malloc for 0 "
				   "domains\n");
			goto error;
		}
		(*d)[0].s = 0;
		(*d)[0].len = 0;
	} else {
		*d = shm_malloc(sizeof(str) * (res->n + 1));
		if(*d == NULL) {
			LM_ERR("ERR:ims_icscf_db_get_nds: failed shm_malloc for %d "
				   "domains\n",
					res->n);
			goto error;
		}
		for(i = 0; i < res->n; i++) {
			s.s = (char *)res->rows[i].values[0].val.string_val;
			s.len = strlen(s.s);
			(*d)[i].s = shm_malloc(s.len);
			if((*d)[i].s == NULL) {
				LM_ERR("ERR:ims_icscf_db_get_nds: failed shm_malloc for %d "
					   "bytes\n",
						s.len);
				(*d)[i].len = 0;
			} else {
				(*d)[i].len = s.len;
				memcpy((*d)[i].s, s.s, s.len);
			}
		}
		(*d)[res->n].s = 0;
		(*d)[res->n].len = 0;
	}

	LM_DBG("INF:ims_icscf_db_get_nds: Loaded %d trusted domains\n", res->n);

	dbf.free_result(hdl_db, res);
	return 1;
error:
	if(res)
		dbf.free_result(hdl_db, res);
	*d = shm_malloc(sizeof(str));
	if(*d == NULL)
		LM_ERR("ERR:ims_icscf_db_get_nds: failed shm_malloc for 0 domains\n");
	else {
		(*d)[0].s = 0;
		(*d)[0].len = 0;
	}
	return 0;
}


static str s_id = {"id", 2};
static str s_s_cscf_uri = {"s_cscf_uri", 10};
/**
 *  Get the S-CSCF names from the database and create the S-CSCF set.
 * @param cap - array of scscf_capabilities to fill with the db contents for the S-CSCF names
 * @returns 1 on success, 0 on error
 */
int ims_icscf_db_get_scscf(scscf_capabilities *cap[])
{

	db_key_t keys_ret[] = {&s_id, &s_s_cscf_uri};
	db_key_t key_ord = &s_id;
	db1_res_t *res = 0;
	str db_table_scscf_str = {
			ims_icscf_db_scscf_table, strlen(ims_icscf_db_scscf_table)};

	int i;

	*cap = 0;

	if(!ims_icscf_db_check_init(hdl_db))
		goto error;

	LM_DBG("DBG:ims_icscf_db_get_scscf: fetching S-CSCFs \n");

	if(dbf.use_table(hdl_db, &db_table_scscf_str) < 0) {
		LM_ERR("ERR:ims_icscf_db_init: cannot select table \"%s\"\n",
				db_table_scscf_str.s);
		goto error;
	}

	if(dbf.query(hdl_db, 0, 0, 0, keys_ret, 0, 2, key_ord, &res) < 0) {
		LM_ERR("ERR:ims_icscf_db_get_scscf: db_query failed\n");
		goto error;
	}

	if(res->n == 0) {
		LM_ERR("ERR:ims_icscf_db_get_scscf:  no S-CSCFs found\n");
		goto error;
	} else {
		*cap = shm_malloc(sizeof(scscf_capabilities) * res->n);
		if(!(*cap)) {
			LM_ERR("ERR:ims_icscf_db_get_scscf: Error allocating %lx bytes\n",
					sizeof(scscf_capabilities) * res->n);
			goto error;
		}
		memset((*cap), 0, sizeof(scscf_capabilities) * res->n);
		for(i = 0; i < res->n; i++) {
			(*cap)[i].id_s_cscf = res->rows[i].values[0].val.int_val;
			(*cap)[i].scscf_name.len =
					strlen(res->rows[i].values[1].val.string_val);
			(*cap)[i].scscf_name.s = shm_malloc((*cap)[i].scscf_name.len);
			if(!(*cap)[i].scscf_name.s) {
				LM_ERR("ERR:ims_icscf_db_get_scscf: Error allocating %d "
					   "bytes\n",
						(*cap)[i].scscf_name.len);
				(*cap)[i].scscf_name.len = 0;
				goto error;
			}
			memcpy((*cap)[i].scscf_name.s,
					res->rows[i].values[1].val.string_val,
					(*cap)[i].scscf_name.len);
		}
	}

	dbf.free_result(hdl_db, res);

	// return the size of scscf set
	return i;

error:
	if(res)
		dbf.free_result(hdl_db, res);
	return 0;
}

static str s_id_s_cscf = {"id_s_cscf", 9};
static str s_capability = {"capability", 10};
/**
 *  Get the S-CSCF capabilities from the database and fill the S-CSCF set.
 * @param cap - array of scscf_capabilities to fill with capabilities
 * @returns 1 on success, 0 on error
 */
int ims_icscf_db_get_capabilities(scscf_capabilities *cap[], int cap_cnt)
{
	db_key_t keys_ret[] = {&s_id_s_cscf, &s_capability};
	db_key_t key_ord = &s_id_s_cscf;
	db1_res_t *res = 0;
	str db_table_capabilities_str = {ims_icscf_db_capabilities_table,
			strlen(ims_icscf_db_capabilities_table)};


	int i, j;
	int ccnt = 0;
	int cnt;


	if(!ims_icscf_db_check_init(hdl_db))
		goto error;

	LM_DBG("DBG:ims_icscf_db_get_capabilities: fetching list of Capabilities "
		   "for I-CSCF\n");

	if(dbf.use_table(hdl_db, &db_table_capabilities_str) < 0) {
		LM_ERR("ERR:ims_icscf_db_init: cannot select table \"%s\"\n",
				db_table_capabilities_str.s);
		goto error;
	}

	if(dbf.query(hdl_db, 0, 0, 0, keys_ret, 0, 2, key_ord, &res) < 0) {
		LM_ERR("ERR:ims_icscf_db_get_capabilities: db_query failed\n");
		goto error;
	}

	if(res->n == 0) {
		LM_DBG("DBG:ims_icscf_db_get_capabilities: No Capabilities found... "
			   "not critical...\n");
		return 1;
	} else {
		for(i = 0; i < cap_cnt; i++) {
			cnt = 0;
			for(j = 0; j < res->n; j++)
				if(res->rows[j].values[0].val.int_val == (*cap)[i].id_s_cscf)
					cnt++;
			(*cap)[i].capabilities = shm_malloc(sizeof(int) * cnt);
			if(!(*cap)[i].capabilities) {
				LM_ERR("ERR:ims_icscf_db_get_capabilities: Error allocating "
					   "%lx bytes\n",
						sizeof(int) * cnt);
				(*cap)[i].cnt = 0;
				goto error;
			}
			cnt = 0;
			for(j = 0; j < res->n; j++)
				if(res->rows[j].values[0].val.int_val == (*cap)[i].id_s_cscf) {
					(*cap)[i].capabilities[cnt++] =
							res->rows[j].values[1].val.int_val;
					ccnt++;
				}
			(*cap)[i].cnt = cnt;
		}
	}
	LM_DBG("INF:ims_icscf_db_get_capabilities: Loaded %d capabilities for %d "
		   "S-CSCFs (%d invalid entries in db)\n",
			ccnt, cap_cnt, res->n - ccnt);
	dbf.free_result(hdl_db, res);
	return 1;

error:
	if(res)
		dbf.free_result(hdl_db, res);
	return 0;
}
